#
# $ds = DataSource->new( mkt_types => [ "sii", "otc" ] );
# $ds->source( source => source_type, year => 90, season => 3, month => 6 );
#
#

package DataSource;
use strict;
use warnings;
use LWP::UserAgent;
use Retriever;
use JSON;
use Encode;

sub new {
    my $invocant = shift;
    my $class = ref($invocant) || $invocant;
    my $self = {};
    bless ($self, $class);
    $self->_init(@_);
    return $self;
}

sub _init {
    my $self = shift;
    my %param = @_;
    $self->{req_param} = {
        encodeURIComponent	=> 1,
        step	=> 1,
        firstin	=> 1,
        TYPEK	=> ref $param{mkt_types} eq "ARRAY" ? $param{mkt_types} : ["sii", "otc"],	# 上市, 上櫃
    };
    my $r = new Retriever;
    
    # get industry type code
    $r->prepare( GET => "http://mops.twse.com.tw/mops/web/t51sb01" );
    print STDERR "retrieving industry type codes...\n";
    $r->retrieve;
    my @ind_types;
    foreach my $row ( @{$r->extract_select( name => "code" )} ){	# parse industry type code
        push ( @ind_types, @$row[0] );
    }
    $self->{req_param}->{code} = \@ind_types;
    
    # get settings
    my $dslist;
    open DSLIST, "<", "source_list.txt";
    while ( my $line = <DSLIST> ){
        $dslist .= $line;
    }
    close DSLIST;
    $self->{_source_list} = JSON->new->utf8->decode( $dslist );
    # encode some strings to utf8
    foreach my $source ( values %{$self->{_source_list}} ){
        $source->{description} = encode("utf8", $source->{description});
        foreach my $cname ( keys %{$source->{columns}} ){
            $source->{columns}->{encode("utf8", $cname)} = delete $source->{columns}->{$cname};
        }
    }
}

sub source {
    my $self = shift;
    my %param = @_;
    if ( defined $param{source} ){
        $self->{_source} = $self->{_source_list}->{$param{source}};
    } else {
        die "source not specified.\n";
    }
    $self->{req_param}->{year} = $param{year};
    $self->{req_param}->{season} = sprintf( "%02d", $param{season} ) if defined $param{season};
    $self->{req_param}->{month} = $param{month};

    # generate list of requests
    my @req_params = ( "" );
    foreach my $tag ( @{$self->{_source}->{para_tags}} ){
        if ( ! defined $self->{req_param}->{$tag} ){
            die "parameter \"$tag\" in request not specified.\n";
        } elsif ( ref $self->{req_param}->{$tag} eq "ARRAY" ){
            my @params_new;
            foreach my $p ( @req_params ){
                foreach my $val ( @{$self->{req_param}->{$tag}} ){
                    push ( @params_new, $p.$tag."=".$val."&" );
                }
            }
            @req_params = @params_new;
        } else {
            foreach my $p ( @req_params ){
                $p .= $tag."=".$self->{req_param}->{$tag}."&";
            }
        }
    }
#@req_params = @req_params[0..1];	# for test

    # conditional adjustment
    if ( $param{source} ne "ors" ){

    foreach my $p ( @req_params ){
        if ( $param{source} eq "div" ){
            if ( $p =~ /year=(\d+)/i ){
                my $ad = $1 + 1911;
                $p =~ s/year=$1/year=$ad/i;
            }
            $p =~ s/step=(\w+)/step=show/i;
        }
        my $req;
        if ( $self->{_source}->{method} eq "POST" ){
            $req = HTTP::Request->new( POST => $self->{_source}->{url} );
            $req->content( $p );
        } else {
            $req = HTTP::Request->new( GET => $self->{_source}->{url}."?".$p );
        }
        push ( @{$self->{_reqs}}, $req );
    }
    
    } else {
        foreach my $mcode ( @{$self->{req_param}->{TYPEK}} ){
            my $req = HTTP::Request->new( GET => "http://mops.twse.com.tw/t21/".$mcode."/t21sc03_".$param{year}."_".$param{month}.".html" );
            push ( @{$self->{_reqs}}, $req );
        }
    }
}

sub reqs {
    my $self = shift;
    return $self->{_reqs};
}

sub desc {
    my $self = shift;
    if (defined $self->{_source}){
        return $self->{_source}->{description};
    }
}

sub columns {
    my $self = shift;
    if (defined $self->{_source}){
        return $self->{_source}->{columns};
    }
}

sub attr {
    my $self = shift;
    if (defined $self->{_source}){
        return $self->{_source}->{attr};
    }
}

sub code {
    my $self = shift;
    if (defined $self->{_source}){
        return $self->{_source}->{decode};
    }
}

1;
